package com.quartz;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicBoolean;

public class StdScheduler implements Scheduler{

	private static final int QUEUE_SIZE = 100;
	
	private static final int THREAD_SIZE = 10;
	
	private Map<String,JobDetail> details = new HashMap<String,JobDetail>();
	
	private PriorityQueue<JobTrigger> tq;
	
	private Executor executor = new ScheduledThreadPoolExecutor(THREAD_SIZE);
	
	public StdScheduler(){
		Comparator<JobTrigger> cm = new Comparator<JobTrigger>() {
			
			@Override
			public int compare(JobTrigger o1, JobTrigger o2) {
				return o1.getNextFireTime().compareTo(o2.getNextFireTime());
			}
		};
		tq = new PriorityQueue<JobTrigger>(QUEUE_SIZE,cm);
	}
	@Override
	public void schedule(JobDetail detail, JobTrigger trigger) {
//		triggers.add(trigger);
		details.put(detail.getId(),detail);
		tq.add(trigger);
	}

	class SchedulerThread extends Thread{

		private AtomicBoolean halted = new AtomicBoolean(false);
		@Override
		public void run() {
			
			while(!halted.get()){
				JobTrigger trigger = nextFireTrigger();
			
				Date nextFireTime = trigger.getNextFireTime();
				
				long time = (nextFireTime.getTime() - System.currentTimeMillis())/1000l;
				
				while(time>1){
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					time = (nextFireTime.getTime() - System.currentTimeMillis())/1000l;
				}
				
				if(trigger(trigger)){
					JobShell shell = new JobShell(details.get(trigger.getJobId()));
					
					executor.execute(shell);
				}
			}
			
		}
	}
	
	class JobShell implements Runnable{

		private JobDetail jobDetail;
		
		public JobShell(JobDetail jobDetail){
			this.jobDetail = jobDetail;
		}
		@Override
		public void run() {
			Class<? extends Job> clazz = jobDetail.get_class();
			
			try {
				Job job = clazz.newInstance();
				
				JobContext context = new JobContext();
				
				job.execute(context);
			} catch (InstantiationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (JobExecuteException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			
		}
		
	}
	
	private JobTrigger nextFireTrigger(){
		return this.tq.poll();
	}
	
	private boolean trigger(JobTrigger trigger){
		Date old = trigger.getNextFireTime();
		long newT = old.getTime() + 2000l;
		
		JobTrigger ntrigger = new JobTrigger();
		ntrigger.setJobId(trigger.getJobId());
		ntrigger.setJobName(trigger.getJobName());
		ntrigger.setNextFireTime(new Date(newT));
		return tq.add(ntrigger);
	}
	@Override
	public void start() {
		executor.execute(new SchedulerThread());
	}
}
